/********************************************************************************
 * Project Name		[JavaEE_JDBC]
 * File Name     	[TestScrollableAndUpdatableResultSet.java]
 * Creation Date 	[2015-01-01]
 * 
 * Copyright© ge.y.yang@gmail.com All Rights Reserved
 * 
 * Work hard, play harder, think big and keep fit
 ********************************************************************************/
package jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import utils.jdbc.JdbcUtils;

/**
 * JDBC可滚动的结果集<br/>
 * 
 * 利用可滚动的结果集可以实现JDBC的分页<br/>
 * 
 * 在JDBC的ResultSet类中, 我们可以使用next()方法从结果集中的一条记录移动到下一条记录<br/>
 * 同时还可以在定义Statement或PrepareStatement时指定结果集是否可滚动或更新<br/>
 * 
 * 以下是注意事项: <br/>
 * 1. 即使使用了CONCUR_UPDATABLE参数来创建Statement, 得到的结果集也不一定是"可更新的"<br/>
 * 如果你的记录集来自合并查询, 这样的结果集就可能是不可更新的. 可以使用ResultSet类的getConcurrency()方法来确定是否为可更新结果集
 * <br/>
 * 
 * 2. 如果使用的JDBC版本是3.0(jdk 1.4以后的版本), 使用CallableStatement的setXXX()方法来设置IN参数时,
 * 既可以使用索引, 也可以使用参数名称. 如果使用JDBC2.0及以下版本, 只能使用使用索引. 对于对应的getXXX()中的参数也一样<br/>
 * 
 * ResultSet可取值常量值:<br/>
 * TYPE_FORWARD_ONLY 结果集不能滚动<br/>
 * TYPE_SCROLL_INSENSITIVE 结果集可以滚动, 但对数据库变化不敏感<br/>
 * TYPE_SCROLL_SENSITIVE 结果集可以滚动, 但对数据库变化敏感<br/>
 * 
 * CONCUR_READ_ONLY 结果集不能用于更新数据库(默认值)<br/>
 * CONCUR_UPDATABLE 结果集可以用于更新数据库<br/>
 * 
 * @author 不落的太阳(Sean Yang aka ShortPeace)
 * @version 1.0
 * @since jdk 1.8
 * 
 */
public class TestScrollableAndUpdatableResultSet {

	private Connection connection;
	private PreparedStatement statement;
	private ResultSet resultSet;

	@Before
	public void init() throws Exception {
		connection = JdbcUtils.getInstance().getConnection();
	}

	/**
	 * 测试可滚动的结果集
	 * 
	 * @throws Exception
	 */
	@Test
	public void testScrollableResultSet() throws Exception {
		String sql = "SELECT User.* FROM User";
		// ResultSet.TYPE_SCROLL_SENSITIVE
		// 在ResultSet中可以随心所欲的先前向后移动游标,
		// 在ResultSet的值有所改变的时候, 它可以得到改变后的最新的值
		statement = connection.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);

		resultSet = statement.executeQuery();
		resultSet.last();
		while (resultSet.previous()) {
			System.out.println(resultSet.getInt("id") + "-" + resultSet.getString("username"));
		}

		System.out.println("移动到首行" + resultSet.first());
		System.out.println("移动到尾行" + resultSet.last());
		System.out.println("是否是首行" + resultSet.isFirst());
		System.out.println("是否是尾行" + resultSet.isLast());
		System.out.println("定位到第二行" + resultSet.absolute(2));
	}

	/**
	 * 测试可更新的结果集
	 * 
	 * @throws Exception
	 */
	@Test
	public void testUpdateableResultSet() throws Exception {
		String sql = "SELECT * FROM User";
		// ResultSet.CONCUR_UPDATABLE
		// 在ResultSet中的数据记录可以任意修改, 然后更新会数据库
		statement = connection.prepareStatement(sql, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);

		resultSet = statement.executeQuery();
		// 使用可更新的结果集
		System.out.println("****************************************修改记录****************************************");
		resultSet.absolute(1);// 指针移到第一条数据
		resultSet.updateString("gender", "F"); // 修改gender列
		resultSet.updateString(7, "update by Sean");// 修改description列
		resultSet.cancelRowUpdates();// 取消当前记录的修改
		showResultSet(resultSet);

		System.out.println("****************************************修改记录****************************************");
		resultSet.absolute(1);
		resultSet.updateInt("age", 10);// 修改age列
		resultSet.updateString(7, "update by Sean");// 修改description列
		resultSet.updateRow();// 将修改提交到数据库
		showResultSet(resultSet);

		System.out.println("****************************************插入记录****************************************");
		resultSet.moveToInsertRow();
		resultSet.updateString("username", "ShortPeace");
		resultSet.updateString("password", "12345");
		resultSet.updateInt("age", 100);
		resultSet.updateString("gender", "M");
		resultSet.updateDate("birthday", new java.sql.Date(new java.util.Date().getTime()));
		resultSet.insertRow();// 将插入提交到数据库
		showResultSet(resultSet);

		System.out.println("****************************************删除记录****************************************");
		resultSet.last();// 游标跳到最后一条记录
		resultSet.deleteRow();
		showResultSet(resultSet);
	}

	/**
	 * 显示一个ResultSet结果集, 在显示之前必须保证它所在的Statement是活着的
	 * 
	 * @param resultSet
	 */
	private static void showResultSet(ResultSet resultSet) throws Exception {
		if (resultSet == null) {
			return;
		}
		ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
		int columnCount = resultSetMetaData.getColumnCount();// 获得该ResultSet中的列数

		// 如果结果集的指针没有位于第一条记录的前面,将结果集的指针指向第一条记录的前面
		if (!resultSet.isBeforeFirst()) {
			resultSet.beforeFirst();
		}
		// 从前往后的移动结果集指针, 处理每条记录
		while (resultSet.next()) {
			for (int i = 1; i <= columnCount; i++) {
				// 由于不知道该列的类型, 所以用getObject方法获取值
				System.out.print(resultSet.getObject(i) + "\t");
			}
			System.out.print(resultSet.getObject(columnCount) + "\r\n");
		}
		resultSet.beforeFirst();
	}

	@After
	public void destroy() {
		JdbcUtils.getInstance().close(resultSet, statement, connection);
	}
}
